#include "xnserial.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

CXnSerial::CXnSerial()
{
	m_fdSerial = -1;
}

CXnSerial::~CXnSerial()
{
	closeDevice();
}

int CXnSerial::openDevice(const char *pDeviceName, int iSpeed, int iBlock, int iRtscts, int iBits, int iStopBits, int iParity)
{
	m_iSpeed = iSpeed;
	m_iBlock = iBlock;
	m_iRtscts = iRtscts;
	m_iBits = iBits;
	m_iStopBits = iStopBits;
	m_iParity = iParity;

	struct termios buf;

	if(m_fdSerial != -1)
	{
		return -1;
	}

	m_fdSerial = open(pDeviceName, O_RDWR | O_NOCTTY | O_NDELAY);
	if(m_fdSerial < 0) 
	{
		return -2;
	}

	if(tcgetattr(m_fdSerial, &m_resetCommCfg) < 0)
	{ 
		return -3; 
	}

	buf = m_resetCommCfg;
	buf.c_cflag = iSpeed | CLOCAL | CREAD;
	if(iRtscts == 1)  buf.c_cflag |= CRTSCTS;
	if(iBits   == 7) 
	{
		buf.c_cflag |= CS7;
	}
	else             
	{
		buf.c_cflag |= CS8;
	}

	if(iStopBits == 2)  
	{
		buf.c_cflag |= CSTOPB;
	}

	if(iParity == CXnSerial::ODD)  buf.c_cflag |= (PARENB | PARODD);
	if(iParity == CXnSerial::EVEN) buf.c_cflag |= PARENB;

	buf.c_lflag = IEXTEN; //ICANON;
	buf.c_oflag     = OPOST;
	buf.c_cc[VMIN]  = 1;
	buf.c_cc[VTIME] = 0;
	buf.c_line      = 0;
	buf.c_iflag     = IGNBRK | IGNPAR | IXANY;
	if(tcsetattr(m_fdSerial, TCSAFLUSH, &buf) < 0)
	{
		return -4;
	}

	if(iBlock == 1) 
	{
		fcntl(m_fdSerial, F_SETFL, fcntl(m_fdSerial, F_GETFL, 0) & ~O_NONBLOCK);
	}
	tcflush(m_fdSerial, TCIOFLUSH);

	return 0;
}

int CXnSerial::openDeviceAgain(const char *pDeviceName)
{
	if (0 == pDeviceName)
	{
		return -10;
	}

	return openDevice(pDeviceName, m_iSpeed, m_iBlock, m_iRtscts, m_iBits, m_iStopBits, m_iParity);
}

int CXnSerial::closeDevice()
{
	if(m_fdSerial == -1) 
	{
		return -1;
	}

	if(::tcsetattr(m_fdSerial, TCSANOW, &m_resetCommCfg) < 0) 
	{
		return -2;
	}

	::close(m_fdSerial);
	m_fdSerial = -1;	

	return 0;
}

int CXnSerial::select(int iTimeout)
{
	fd_set wset,rset,eset;
	int    ret,maxfdp1;

	if(iTimeout <= 0) return 1;
	/* setup sockets to read */
	maxfdp1 = m_fdSerial + 1;
	FD_ZERO(&rset);
	FD_SET (m_fdSerial, &rset);
	FD_ZERO(&wset);
	FD_ZERO(&eset);
	timout.tv_sec  =  iTimeout / 1000;
	timout.tv_usec = (iTimeout % 1000) * 1000;

	ret = ::select(maxfdp1, &rset ,&wset, &eset, &timout);
	if(ret == 0) 
	{
		return 0; /* timeout */
	}

	return 1;
}

int CXnSerial::readChar()
{
	int  ret;
	unsigned char buf[2];

	if(m_fdSerial == -1) 
	{
		return -1;
	}

	ret = read(m_fdSerial, buf, 1);
	if(ret == 1)
	{
		return buf[0];
	}

	if(ret == 0) 
	{
		return -2;
	}

	return -3;
}

int CXnSerial::readBlock(unsigned char *pBuf, int iLen, int iTimeout)
{
	int  ret;
	if(m_fdSerial == -1) 
	{
		return -1;
	}

	if(!pBuf) 
	{
		return -2;
	}

	if (iLen < 0)
	{
		return -3;
	}

	if(iTimeout >= 0)
	{
		if(select(iTimeout) == 0)
		{
			return -4;
		}
	}
	ret = read(m_fdSerial, pBuf, iLen);
	if(ret == -1)
	{
		return -5;
	}
	return ret;	
}

int CXnSerial::writeChar(unsigned char uchar)
{
	int ret;
	if(m_fdSerial == -1) 
	{
		return -1;
	}
	ret = write(m_fdSerial, &uchar, 1);
	if(ret < 0)
	{
		return -2;
	}

	return ret;
}

int CXnSerial::writeBlock(const unsigned char *pBuf, int iLen)
{
	int ret;

	if(m_fdSerial == -1) 
	{
		return -1;
	}
	
	ret = write(m_fdSerial, pBuf, iLen);

	if (ret < 0)
	{
		ret = -2;
	}
	
	return ret;
}

int CXnSerial::readLine(unsigned char *pBuf, int iMaxLen, int iTimeout)
{
	int i,c,ret;

	if (!pBuf)
	{
		return -1;
	}
	
	if(iMaxLen <= 1) 
	{
		return -2;
	}

	ret = 0;
	pBuf[iMaxLen - 1] = '\0';

	for(i = 0; i < iMaxLen - 2; ++i)
	{
		ret = i;
		if(iTimeout > 0)
		{
			if(0 == select(iTimeout)) 
			{
				return -3;
			}
		}
		c = readChar();
		if(c < 0)
		{
			pBuf[i] = '\0';
			ret = -4;
			break;
		}

		pBuf[i] = (unsigned char) c;
		ret = i + 1;  // 添加字符成功
		if(c < ' ' && c != '\t')
		{
			pBuf[i + 1] = '\0';			
			break;
		}
	}

	return ret;
}
